/**
 * 用户处理模块
 */
const { validationResult } = require('express-validator');
const { hexHash } = require('@root/utils/md5');
const uuid = require('@root/utils/uuid');
const { validateParams } = require('@root/utils/peak-tools.js');
const { poolPromise } = require('@root/db/index');
const token_manager = require('@root/utils/jwt');
const config = require('@root/config/index');
const { httpStatusCode } = require('@root/types/HttpStatusCode.js');

/**
 * 检查用户名
 */
async function checkUsername(req, res, next) {
  try {
    if (validateParams({ req, res, validationResult })) return;

    const { username } = req.query;
    const selectSql = `select username from sys_user where username=?`;
    let [result] = await poolPromise.query(selectSql, [username]);
    if (result.length) {
      return res.status(httpStatusCode.Conflict).json({
        code: httpStatusCode.Conflict,
        msg: '用户名已存在',
        data: '用户名已存在',
      });
    }
    res.status(httpStatusCode.Ok).json({
      code: httpStatusCode.Ok,
      msg: '用户名可用',
      data: '用户名可用',
    });
  } catch (error) {
    next(error);
  }
}

/**
 * 注册回调
 */
async function register(req, res, next) {
  if (validateParams({ req, res, validationResult })) return;

  // 校验通过
  let { username, password, nickname } = req.body;
  const connection = await poolPromise.getConnection(); // 获取一个连接对象
  try {
    // 查询该账号是否存在
    const select_sql = `select * from sys_user where username = '${username}'`;
    let [result] = await connection.query(select_sql);
    if (result?.length > 0) {
      res.status(httpStatusCode.Conflict).json({
        code: httpStatusCode.Conflict,
        msg: '用户名已存在,请修改用户名!',
        data: null,
      });
    } else {
      // 不存在则注册
      await connection.beginTransaction(); // 开启事务
      password = hexHash(password); // 加密存储密码
      const insert_user = `insert into sys_user (nickname,username,password) values(?,?,?)`;
      let [{ insertId, affectedRows: user_affectedRows }] = await connection.execute(insert_user, [
        nickname,
        username,
        password,
      ]);
      if (user_affectedRows == 0) {
        await connection.rollback(); // 回滚事务
        return res.status(httpStatusCode.InternalServerError).json({
          code: httpStatusCode.InternalServerError,
          msg: '注册账号失败',
          data: null,
        });
      }
      // 新账户分配默认角色
      const insert_user_role = `insert into sys_user_role (user_id,role_id) values (?,?)`;
      let [{ affectedRows: user_role_affectedRows }] = await connection.execute(insert_user_role, [
        insertId,
        config.CommonUserId,
      ]);
      if (user_role_affectedRows == 0) {
        await connection.rollback(); // 回滚事务
        return res.status(httpStatusCode.InternalServerError).json({
          code: httpStatusCode.InternalServerError,
          msg: '分配角色失败',
          data: null,
        });
      }

      await connection.commit(); // 提交事务
      res.status(httpStatusCode.Ok).json({
        code: httpStatusCode.Ok,
        msg: '注册成功',
        data: null,
      });
    }
  } catch (error) {
    await connection.rollback(); // 回滚事务
    next(error);
  } finally {
    connection.release(); // 释放连接对象
  }
}

/**
 * 登录回调
 */
async function login(req, res, next) {
  try {
    if (validateParams({ req, res, validationResult })) return;

    // 校验通过
    let { username, password } = req.body;
    password = hexHash(password); // 加密
    const selectSql = `select user_id ,nickname, state from sys_user where username=? and password=?`;
    const [result] = await poolPromise.query(selectSql, [username, password]);
    if (!result?.length) {
      return res.status(httpStatusCode.BadRequest).json({
        code: httpStatusCode.BadRequest,
        msg: '用户名或密码错误',
        data: null,
      });
    }
    const { user_id, state, nickname } = result[0];
    // 判断用户状态
    if (state === 2) {
      return res.status(httpStatusCode.Forbidden).json({
        code: httpStatusCode.Forbidden,
        msg: '用户被禁用, 请联系管理员',
        data: null,
      });
    }
    const select_user_roles = `select role_id from sys_user_role where user_id = ?`;
    const [roles] = await poolPromise.execute(select_user_roles, [user_id]);
    if (!roles.length) {
      return res.status(httpStatusCode.Forbidden).json({
        code: httpStatusCode.Forbidden,
        msg: '当前用户未分配角色, 请联系管理员',
        data: null,
      });
    }

    // 生成payload(负载)
    const payload = { user_id, roles, username, nickname };
    // 生成 access_token 访问令牌
    const access_token = token_manager.create_access_token({ ...payload, type: 'access' });
    // 生成 refresh_token 刷新令牌
    const jwtid = uuid();
    const refresh_token = token_manager.create_refresh_token(
      { ...payload, type: 'refresh' },
      { jwtid }
    );

    // 存储新的refresh_token
    const update_refresh_sql = `update sys_user set refresh_token = ?,jwt_id = ? where user_id = ?`;
    const [{ affectedRows: refresh_token_rows }] = await poolPromise.query(update_refresh_sql, [
      refresh_token,
      jwtid,
      user_id,
      username,
    ]);
    if (refresh_token_rows === 0) {
      return res.status(httpStatusCode.BadRequest).json({
        code: httpStatusCode.BadRequest,
        msg: 'refresh_token存储失败',
        data: null,
      });
    }

    res.status(httpStatusCode.Ok).json({
      code: httpStatusCode.Ok,
      msg: '登录成功',
      data: {
        refresh_token,
        access_token,
        user_id,
        username,
      },
    });
  } catch (error) {
    next(error);
  }
}

/**
 * 登出回调
 */
async function logout(req, res, next) {
  try {
    let { user_id } = req.auth;
    const sql = `update sys_user set refresh_token = null,jwt_id = null where user_id = ?`;
    const [{ affectedRows }] = await poolPromise.execute(sql, [user_id]);
    if (!affectedRows) {
      return res.status(httpStatusCode.InternalServerError).json({
        code: httpStatusCode.InternalServerError,
        msg: '登出失败',
        data: null,
      });
    }

    res.status(httpStatusCode.Ok).json({
      code: httpStatusCode.Ok,
      msg: '登出成功',
    });
  } catch (error) {
    next(error);
  }
}

/**
 * 获取用户列表
 */
async function selectUserList(req, res, next) {
  try {
    const { username = '', nickname = '' } = req.query;
    const select_user_list_sql = `select u.*, if(count(u_r.role_id), json_arrayagg(json_object('role_id', r.role_id, 'role_name', r.role_name, 'role_key', r.role_key, 'role_sort', r.role_sort, 'remark', r.remark)), json_array()) roles from sys_user u left join sys_user_role u_r on u.user_id = u_r.user_id left join sys_role r on r.role_id = u_r.role_id where u.username like '%${username}%' and u.nickname like '%${nickname}%' group by u.user_id`;
    let [userList] = await poolPromise.execute(select_user_list_sql);
    res.status(httpStatusCode.Ok).json({
      code: httpStatusCode.Ok,
      msg: '获取用户列表成功',
      data: userList,
    });
  } catch (error) {
    next(error);
  }
}

/**
 * 获取用户信息
 */
async function selectUser(req, res, next) {
  try {
    // 不传递user_id则获取自己
    let { user_id } = req.query;
    user_id = user_id || req.auth.user_id;
    const sql = `select user_id,username,nickname,avatar,sex,age,address,phone,email,state,create_time,login_time,update_time from sys_user where user_id = ?`;
    let [[useInfo]] = await poolPromise.query(sql, [user_id]);
    if (!useInfo) {
      return res.status(httpStatusCode.InternalServerError).json({
        code: httpStatusCode.InternalServerError,
        msg: '获取用户信息不存在',
        data: null,
      });
    }

    const select_role_sql = `select r.* from sys_role r left join sys_user_role u_r on r.role_id = u_r.role_id where user_id = ?`;
    let [roles] = await poolPromise.execute(select_role_sql, [user_id]);
    if (!roles.length) {
      return res.status(httpStatusCode.Conflict).json({
        code: httpStatusCode.Conflict,
        msg: '当前用户未分配角色',
        data: null,
      });
    }

    const roleIds = roles.map((item) => item.role_id);
    const roleKeys = roles.map((item) => item.role_key);
    if (roleKeys.includes(config.SuperAdmin)) {
      // 超级管理员拥有所有权限
      useInfo.permissions = ['*:*:*'];
    } else {
      const permissions_sql = `
      select distinct m.perms from sys_menu m
          left join sys_role_menu r_m on m.menu_id = r_m.menu_id
          where r_m.role_id in (${roleIds.map(() => '?').join(',')})`;
      let [permissions] = await poolPromise.execute(permissions_sql, roleIds);
      useInfo.permissions = permissions.map((item) => item.perms);
    }
    res.status(httpStatusCode.Ok).json({
      code: httpStatusCode.Ok,
      msg: '获取用户信息成功',
      data: { ...useInfo, roles },
    });
  } catch (error) {
    next(error);
  }
}

/**
 * 后台创建用户
 */
async function insertUser(req, res, next) {
  if (validateParams({ req, res, validationResult })) return;
  const connection = await poolPromise.getConnection();
  try {
    const {
      username,
      nickname,
      avatar = '',
      sex = 'u',
      age = 0,
      address = '',
      phone = '',
      email = '',
      state = 2,
      roleIds = [],
    } = req.body;
    const select_user_sql = `select username from sys_user where username = ?`;
    let [list] = await poolPromise.execute(select_user_sql, [username]);
    if (list.length) {
      return res.status(httpStatusCode.Conflict).json({
        code: httpStatusCode.Conflict,
        msg: '用户名已存在',
        data: null,
      });
    }
    await connection.beginTransaction();
    const password = hexHash('qwe123'); // 后台创建是默认密码
    const insert_user_sql = `insert into sys_user (username,password,nickname,avatar,sex,age,address,phone,email,state) values(?,?,?,?,?,?,?,?,?,?)`;
    let [{ affectedRows, insertId: user_id }] = await connection.execute(insert_user_sql, [
      username,
      password,
      nickname,
      avatar,
      sex,
      age,
      address,
      phone,
      email,
      state,
    ]);
    if (!affectedRows) {
      await connection.rollback(httpStatusCode.InternalServerError);
      return res.status().json({
        code: httpStatusCode.InternalServerError,
        msg: '添加用户失败',
        data: null,
      });
    }

    if (roleIds.length) {
      const role_placeholder = roleIds.map(() => '(?,?)').join(',');
      const insert_ids = roleIds.flatMap((role_id) => [user_id, role_id]);
      const insert_user_role_sql = `insert into sys_user_role (user_id,role_id) values ${role_placeholder}`;
      let [{ affectedRows: role_affectedRows }] = await connection.execute(
        insert_user_role_sql,
        insert_ids
      );
      if (!role_affectedRows) {
        await connection.rollback();
        return res.status(httpStatusCode.InternalServerError).json({
          code: httpStatusCode.InternalServerError,
          msg: '添加用户角色失败',
          data: null,
        });
      }
    }

    await connection.commit();
    res.status(httpStatusCode.Ok).json({
      code: httpStatusCode.Ok,
      msg: '添加用户成功',
      data: null,
    });
  } catch (error) {
    await connection.rollback();
    next(error);
  } finally {
    connection.release();
  }
}

/**
 * 修改用户资料
 */
async function updateUser(req, res, next) {
  try {
    if (validateParams({ req, res, validationResult })) return;
    let {
      user_id,
      nickname,
      sex,
      state,
      age,
      avatar,
      address,
      phone,
      email,
      roleIds = [],
    } = req.body;
    const sql = `update sys_user set nickname = ?,avatar = ?,state = ?,sex = ?,age = ?,address = ?,phone = ?,email = ? where user_id = ?`;
    let [{ affectedRows }] = await poolPromise.execute(sql, [
      nickname,
      avatar,
      state,
      sex,
      age,
      address,
      phone,
      email,
      user_id,
    ]);
    if (!affectedRows) {
      return res.status(409).json({
        code: 409,
        msg: '修改用户信息失败',
        data: null,
      });
    }
    const delete_user_role_sql = `delete from sys_user_role where user_id = ?`;
    await poolPromise.execute(delete_user_role_sql, [user_id]);
    if (roleIds.length) {
      const role_placeholder = roleIds.map(() => '(?,?)').join(',');
      const insert_ids = roleIds.flatMap((role_id) => [user_id, role_id]);
      const insert_user_role_sql = `insert into sys_user_role (user_id,role_id) values ${role_placeholder}`;
      await poolPromise.execute(insert_user_role_sql, insert_ids);
    }

    res.status(httpStatusCode.Ok).json({
      code: httpStatusCode.Ok,
      msg: '修改用户信息成功',
      data: null,
    });
  } catch (error) {
    next(error);
  }
}

/**
 * 修改密码
 */
async function updatePassword(req, res, next) {
  if (validateParams({ req, res, validationResult })) return;

  try {
    const { username, oldPassword, newPassword } = req.body;
    const querySql = `select id,username from user where username=? and password=?`;
    // 查询输入的账号密码是否正确
    const [userList] = await poolPromise.query(querySql, [username, hexHash(oldPassword)]);
    if (!userList?.length) {
      return res.status(httpStatusCode.BadRequest).json({
        code: httpStatusCode.BadRequest,
        msg: '用户名或密码错误',
        data: null,
      });
    }

    const updateSql = `update user set password=? where username=? and password=?`;
    let [{ affectedRows }] = await poolPromise.query(updateSql, [
      hexHash(newPassword),
      username,
      hexHash(oldPassword),
    ]);
    if (affectedRows == 0) {
      return res.status(httpStatusCode.InternalServerError).json({
        code: httpStatusCode.InternalServerError,
        msg: '重置密码失败',
        data: null,
      });
    }
    res.status(httpStatusCode.Ok).json({
      code: httpStatusCode.Ok,
      msg: '重置密码成功',
      data: null,
    });
  } catch (error) {
    next(error);
  }
}

/**
 * 删除用户
 */
async function deleteUser(req, res, next) {
  try {
    if (validateParams({ req, res, validationResult })) return;

    const { ids } = req.body;
    const delete_user_sql = `delete from sys_user where user_id in (${new Array(ids.length)
      .fill('?')
      .join(',')})`;
    let [{ affectedRows }] = await poolPromise.execute(delete_user_sql, ids);
    if (!affectedRows) {
      return res.status(httpStatusCode.InternalServerError).json({
        code: httpStatusCode.InternalServerError,
        msg: '用户删除失败',
        data: null,
      });
    }

    res.status(httpStatusCode.Ok).json({
      code: httpStatusCode.Ok,
      msg: '用户删除成功',
      data: null,
    });
  } catch (error) {
    next(error);
  }
}

module.exports = {
  checkUsername,
  login,
  register,
  logout,
  selectUserList,
  selectUser,
  insertUser,
  updateUser,
  updatePassword,
  deleteUser,
};
